package top.lingma.algorithm;

import lombok.Data;

import java.io.Serializable;

/**
 * @author yui
 * @date 2019年05月22日 上午11:02
 */
@Data
public class RedisPermits implements Serializable {

    /**
     * maxPermits 最大存储令牌数
     */
    private Long maxPermits;
    /**
     * storedPermits 当前存储令牌数
     */
    private Long storedPermits;

    /***
     * 每周期个数
     */
    private Long permitsPerSecond;

    /**
     * nextFreeTicketMillis 下次请求可以获取令牌的起始时间，默认当前系统时间
     */
    private Long nextFreeTicketMillis;


    /**
     * @param permitsPerSecond 每秒放入的令牌数
     * @param maxBurstSeconds  maxPermits由此字段计算，最大存储maxBurstSeconds秒生成的令牌
     */
    public RedisPermits(Long permitsPerSecond, Integer maxBurstSeconds) {
        if (null == maxBurstSeconds||permitsPerSecond<1) {
            maxBurstSeconds = 60;
        }
        this.permitsPerSecond = permitsPerSecond;
        this.maxPermits = (permitsPerSecond * maxBurstSeconds);
        this.storedPermits = permitsPerSecond.longValue();

        this.nextFreeTicketMillis = System.currentTimeMillis();
    }




    /***
     * 计算等待时间内能不能拿到令牌
     *  最大等待时间>=下次生成令牌时间+生成令牌时间
     * @param  maxWaitingTime

     * @return
     */
    private boolean canAcquire(long maxWaitingTime) {
        if (storedPermits > 1) {
            return true;
        }
        return maxWaitingTime >= nextFreeTicketMillis;
    }

    /**
     * @return 下一张令牌生成需要多久
     */
    final long reserveAndGetWaitLength() {
        //当前存储数量>=permits 减去存储数量
        if (storedPermits > 1) {
            --storedPermits;
            return 0;
        }
        /**
         * 如果  nextFreeTicketMillis 小于当前时间 说明久无访问
         *
         */
        if (nextFreeTicketMillis < System.currentTimeMillis()) {
            storedPermits += (System.currentTimeMillis() - nextFreeTicketMillis) / 1000 * permitsPerSecond;
            if (storedPermits > maxPermits) {
                storedPermits = maxPermits;
            }
            nextFreeTicketMillis = System.currentTimeMillis();
        }
        nextFreeTicketMillis += 1000;
        storedPermits = (storedPermits + permitsPerSecond);
        if (storedPermits > maxPermits) {
            storedPermits = maxPermits;
        }
        return 1000;
    }

    /***
     *
     * @param maxWaitingTime 最大等待时间
     * @return
     */
    public Boolean acquire(Long maxWaitingTime) {

        //计算当前周期有没有令牌 ,下个周期进行补偿
        if (!canAcquire(System.currentTimeMillis())) {
            return false;
        }
        //等待时间内能拿到 则先占用 本次能拿到下次肯定拿不到
        this.reserveAndGetWaitLength();
        return true;
    }

}
